/*
 * This file is part of JFlowMap.
 *
 * Copyright 2009 Ilya Boyandin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fons.navigator.views.piemap;

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;
import java.util.Collections;
import java.util.List;

import prefuse.data.Node;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import edu.umd.cs.piccolo.PNode;
import fons.navigator.util.GeomUtils;
import fons.navigator.util.map.GeoMap;
import fons.navigator.util.map.MapArea;
import fons.navigator.util.map.MapProjection;
import fons.navigator.util.piccolo.PGeoMapArea;
import fons.navigator.util.piccolo.PNodes;

/**
 * @author Ilya Boyandin
 */
public class PGeoMap extends PNode {

	private static final long serialVersionUID = 1L;
	private final PieMap visualPieMap;
	private final GeoMap mapModel;
	private final MapProjection mapProjection;
	private Rectangle2D boundingBox;

	public PGeoMap(PGeoMap toCopy) {
		this(toCopy.getVisualPieMap(), toCopy.mapModel, toCopy.mapProjection);
	}

	public PGeoMap(PieMap cs, GeoMap mapModel, MapProjection proj) {
		this.mapModel = mapModel;
		this.visualPieMap = cs;
		this.mapProjection = proj;
		for (MapArea area : mapModel.getAreas()) {
			if (!area.isEmpty()) {
				addArea(area);
			}
		}
		setPaint(Color.WHITE);
	}

	public Rectangle2D getBoundingBox() {
		if (boundingBox == null) {
			Rectangle2D b = null;
			for (PGeoMapArea va : PNodes.childrenOfType(this, PGeoMapArea.class)) {
				if (b == null) {
					b = va.getBoundingBox();
				} else {
					b.add(va.getBoundingBox());
				}
			}
			boundingBox = b;
		}
		return (Rectangle2D)boundingBox.clone();
	}

	public PGeoMapArea addArea(MapArea area) {
		return addArea(area, mapProjection);
	}

	public PGeoMapArea addArea(MapArea area, MapProjection proj) {
		PGeoMapArea visualArea = createVisualArea(area, proj);
		addChild(visualArea);
		boundingBox = null;
		return visualArea;
	}

	private PGeoMapArea createVisualArea(MapArea area, MapProjection proj) {
		return new PGeoMapArea(/*this, */area, proj);
	}

	public MapProjection getMapProjection() {
		return mapProjection;
	}

	public PieMap getVisualPieMap() {
		return visualPieMap;
	}

	public void updateColors() {
		for (PGeoMapArea va : PNodes.childrenOfType(this, PGeoMapArea.class)) {
			va.updateColors();
		}
	}

	public PGeoMapArea getVisualAreaBy(String id) {
		for (PGeoMapArea va : PNodes.childrenOfType(this, PGeoMapArea.class)) {
			String vaid = va.getArea().getId();
			if (vaid != null  &&  id.equals(vaid)) return va;
		}
		return null;
	}

	public List<Rectangle2D> createAreasForNodesWithoutCoords(Iterable<Node> nodesWithoutCoords) {
		int num = Iterables.size(nodesWithoutCoords);
		if (num <= 0) {
			return Collections.emptyList();
		}

		List<Rectangle2D> rects = Lists.newArrayList();

		int maxPerRow = 1;
		int numPerRow = Math.min(maxPerRow, num);
		int r = num % numPerRow;

		Rectangle2D bounds = getBoundingBox();
		double rwidth = bounds.getWidth() * 0.4 / (maxPerRow == 1 ? 1 : maxPerRow-1);
		double rheight = (bounds.getHeight() / 10);
		double topMargin = bounds.getHeight() / 5;

		for (int i = 0; i < num; i++) {
			final int numInThisRow = (i >= (num - r) ? r : numPerRow);
			double hcentering = (bounds.getWidth() - (numInThisRow - 1) * rwidth) / 2;

			double x = hcentering + bounds.getMinX() + (i % numPerRow) * rwidth;
			double y = bounds.getMaxY() + topMargin + Math.floor(i / numPerRow) * rheight;

			Rectangle2D rect = new Double(x - rwidth / 2, y - rheight / 3, rwidth, rheight);
			rect = GeomUtils.growRectByRelativeSize(rect, -0.05, -0.1, -0.05, -0.1);

			rects.add(rect);
		}

		assert(rects.size() == num);

		return rects;
	}
}
